﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace ThumbnailProcessingSample
{
    public partial class Form1 : Form
    {
        string sourcePath = @"C:\Users\amit1607\Pictures\pics\Party Pics";
        string targetPath = @"C:\Users\amit1607\Pictures\pics\Thumbs";
        int totalfiles = 0;
        string TimeElapsed = "";
        bool islimited = false;
        bool isQuality = false;
        int fileLimit = 0;
        CancellationTokenSource cancelToken = new CancellationTokenSource();

        public Form1()
        {
            InitializeComponent();

            txtSourcepath.Text = sourcePath;
            txtTargetpath.Text = targetPath;
            
            
            cbQuality.CheckedChanged += new EventHandler((sender, args) => { isQuality = ((CheckBox)sender).Checked; });
            totalfiles = Directory.GetFiles(sourcePath, "*.jpg").Length;
            cmbLimit.SelectedIndex = 0;
            fileLimit = totalfiles;
            ResetUI();
        }

        void ResetUI()
        {
            lblResult.Text = "";
            progressBar1.Value = 0;
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            ResetUI();

            if (cbIsParallel.Checked)
               ProcessFilesInParallel();
            else
                ProcessFilesInNormal();
        }

        private void ProcessFilesInNormal()
        {
            string[] files = Directory.GetFiles(sourcePath, "*.jpg");
            int count = 0;
            totalfiles = files.Length;
            //--- Record the start time---
            DateTime startTime = DateTime.Now;

            foreach (string currentFile in files)
            {
                string filename = Path.GetFileName(currentFile);

                if (isQuality)
                    GenerateThumbnail(filename);
                else
                    GetThumb(filename);

                count++;
                ReportProgress(count);
            }

            //Note the time consumed here
            TimeSpan elapsed = DateTime.Now.Subtract(startTime);
            TimeElapsed = (int)elapsed.TotalSeconds + " s : " + elapsed.Milliseconds + " ms";

            lblResult.Text = "Time Elapsed: " + TimeElapsed;
        }

        private void ProcessFilesInParallel()
        {
            ParallelOptions parOpts = new ParallelOptions();
            parOpts.CancellationToken = cancelToken.Token;
            parOpts.MaxDegreeOfParallelism = Environment.ProcessorCount;

            string[] files = Directory.GetFiles(sourcePath, "*.jpg");
            long count = 0;
            totalfiles = files.Length;
            btnCancel.Visible = true;
            //--- Record the start time---
            DateTime startTime = DateTime.Now;

            try
            {
                Task t1 = Task.Factory.StartNew(() =>
                {
                    try
                    {
                        Parallel.ForEach(files, parOpts, currentFile =>
                        {
                            //Check if cancellation requested
                            if (cancelToken.Token.IsCancellationRequested)
                            {
                                cancelToken.Token.ThrowIfCancellationRequested();
                            }

                            string filename = Path.GetFileName(currentFile);
                            
                            //Threadsafe updation to shared counter
                            count = Interlocked.Increment(ref count);

                            if (islimited && fileLimit <= count)
                            {
                                cancelToken.Cancel();
                               // MessageBox.Show("Limit reached fileLimit = " + fileLimit + " Count=" + count);
                            }

                            if (isQuality)
                                GenerateThumbnail(filename);
                            else
                                GetThumb(filename);

                            //update the progress on UI
                            progressBar1.Invoke((Action)delegate { ReportProgress(count); });
                        });
                    }
                    catch (OperationCanceledException ex)
                    {
                        progressBar1.Invoke((Action)delegate { ReportProgress(0); });
                        MessageBox.Show(ex.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    }

                    //ContinueWith is used to sync the UI when task completed.
                }, cancelToken.Token).ContinueWith((result) =>
                {
                    //Note the time consumed here
                    TimeSpan elapsed = DateTime.Now.Subtract(startTime);
                    TimeElapsed = (int)elapsed.TotalSeconds + " s : " + elapsed.Milliseconds + " ms";

                    //finally update the UI with the summary result
                    lblResult.Invoke((Action)delegate { lblResult.Text = "File Processed: " + count + " out of " + fileLimit + " Time Elapsed: " + TimeElapsed; });
                },new CancellationTokenSource().Token, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
            }
            catch (AggregateException ae)
            {
                MessageBox.Show(ae.InnerException.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        //Callback for GetThumb method used in Bitmap.GetThumnail as parameter
        public bool ThumbnailCallback()
        {
            return false;
        }

        //Generate normal quality thumbnails
        public void GetThumb(string filename)
        {
            Image.GetThumbnailImageAbort myCallback =
            new Image.GetThumbnailImageAbort(ThumbnailCallback);

            using (Bitmap myBitmap = new Bitmap(Path.Combine(sourcePath, filename)))
            {
                myBitmap.GetThumbnailImage(100, 100, myCallback, IntPtr.Zero).Save(Path.Combine(targetPath, filename));
            }
        }

        //Generate best quality thumbnails
        private void GenerateThumbnail(string fileName)
        {
            int thumbWidth = 100;
            int thumbHeight = 100;

            using (System.Drawing.Image image = System.Drawing.Image.FromFile(Path.Combine(sourcePath, fileName)))
            {
                int srcWidth = image.Width;
                int srcHeight = image.Height;

                if (srcHeight > srcWidth)
                    thumbHeight = (srcHeight / srcWidth) * thumbWidth;
                else
                  thumbHeight = (srcWidth / srcHeight) * thumbWidth; 

                using (System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(thumbWidth, thumbHeight))
                {
                    //- Create a System.Drawing.Graphics object from the Bitmap which we will use to draw the high quality scaled image
                    using (System.Drawing.Graphics gr = System.Drawing.Graphics.FromImage(bitmap))
                    {

                        //- Set the System.Drawing.Graphics object property SmoothingMode to HighQuality
                        gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

                        //- Set the System.Drawing.Graphics object property CompositingQuality to HighQuality
                        gr.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;

                        //- Set the System.Drawing.Graphics object property InterpolationMode to High
                        gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;

                        //- Draw the original image into the target Graphics object scaling to the desired width and height
                        System.Drawing.Rectangle rectDestination = new System.Drawing.Rectangle(0, 0, thumbWidth, (int)thumbHeight);
                        //Set Image codec of JPEG type, the index of JPEG codec is "1"

                        System.Drawing.Imaging.ImageCodecInfo codec = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()[1];

                        //Set the parameters for defining the quality of the thumbnail... here it is set to 100%

                        System.Drawing.Imaging.EncoderParameters eParams = new System.Drawing.Imaging.EncoderParameters(1);

                        eParams.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);

                        gr.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, System.Drawing.GraphicsUnit.Pixel);

                        bitmap.Save(Path.Combine(targetPath, fileName));
                    }
                    //====================================== 
                }
            }
        }

        private void ReportProgress(long status)
        {
            int value = ((int)status * 100) / totalfiles;
            progressBar1.Value = value;
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
          cancelToken.Cancel();
        }

        private void cmbLimit_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cmbLimit.GetItemText(cmbLimit.SelectedItem) == "None")
            {
                fileLimit = totalfiles;
                islimited = false;
            }
            else
            {
                if (Convert.ToInt32(cmbLimit.GetItemText(cmbLimit.SelectedItem)) > totalfiles)
                {
                    MessageBox.Show("Files to process in the source directory are less than the limit. \nPlease select lower limit or select \"None\" to process all.", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    if (cmbLimit.SelectedIndex >= 1)
                        cmbLimit.SelectedIndex = cmbLimit.SelectedIndex - 1;
                }
                islimited = true;
                fileLimit = Convert.ToInt32(cmbLimit.GetItemText(cmbLimit.SelectedItem));
            }
           
            
        }
    }
}
